package com.ethink.framework.common.web.advice;

import cn.hutool.extra.spring.SpringUtil;
import com.ethink.framework.common.cache.AbstractCacheMap;
import com.ethink.framework.common.cache.CacheEntity;
import com.ethink.framework.common.constant.CommonCacheCategory;
import com.ethink.framework.common.constant.CommonRespCode;
import com.ethink.framework.common.constant.ResponseCodeEnum;
import com.ethink.framework.common.exception.BusinessException;
import com.ethink.framework.common.exception.DefinitionException;
import com.ethink.framework.common.util.JacksonUtils;
import com.ethink.framework.common.web.response.CommonResponse;
import com.ethink.framework.log.constant.LogConstant;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.util.StringUtils;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.multipart.support.MissingServletRequestPartException;
import org.springframework.web.servlet.NoHandlerFoundException;

import java.nio.file.AccessDeniedException;

/**
 * 异常通用处理
 *
 * @author yunduo
 */
@ResponseBody
@Slf4j
@ControllerAdvice
public class DefaultExceptionAdvice {

    /**
     * 返回状态码:404
     */
    @ResponseStatus(HttpStatus.NOT_FOUND)
    @ExceptionHandler(NoHandlerFoundException.class)
    public CommonResponse noHandlerFoundException(NoHandlerFoundException e) {
        return defHandler(CommonRespCode.SYSTEM_ERR, null, e);
    }

    /**
     * 参数类型转换错误
     * 返回状态码:400
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public CommonResponse handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e) {
        return defHandler(CommonRespCode.PARAM_ERR, null, e);
    }

    /**
     * 字段不能为空
     * 返回状态码:400
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(MissingServletRequestPartException.class)
    public CommonResponse missingServletRequestPartException(MissingServletRequestPartException e) {
        return defHandler(CommonRespCode.PARAM_ERR, null, e);
    }

    /**
     * JSON序列化错误
     * 返回状态码:400
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public CommonResponse httpMessageNotReadableException(HttpMessageNotReadableException e) {
        return defHandler(CommonRespCode.SYSTEM_ERR, null, e);
    }


    /**
     * IllegalArgumentException异常处理返回json
     * 返回状态码:400
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler({IllegalArgumentException.class})
    public CommonResponse badRequestException(IllegalArgumentException e) {
        return defHandler(CommonRespCode.PARAM_ERR, null, e);
    }

    /**
     * validate 插件异常处理
     *
     * @param e
     * @return
     */
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler({MethodArgumentNotValidException.class})
    public CommonResponse methodArgumentNotValidException(MethodArgumentNotValidException e) {
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        sb.append(StringUtils.arrayToDelimitedString(
                e.getBindingResult().getAllErrors().stream().map(ObjectError::getDefaultMessage).toArray(),
                ","));
        sb.append("] ");
        return defHandler(CommonRespCode.PARAM_ERR, sb.toString(), e);
    }

    /**
     * AccessDeniedException异常处理返回json
     * 返回状态码:403
     */
    @ResponseStatus(HttpStatus.FORBIDDEN)
    @ExceptionHandler({AccessDeniedException.class})
    public CommonResponse badMethodExpressException(AccessDeniedException e) {
        return defHandler(CommonRespCode.ACCESS_DENIED, null, e);
    }

    /**
     * 返回状态码:405
     */
    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    @ExceptionHandler({HttpRequestMethodNotSupportedException.class})
    public CommonResponse handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
        return defHandler(CommonRespCode.HTTP_METHOD_NOT_SUPPORT, null, e);
    }

    /**
     * 返回状态码:415
     */
    @ResponseStatus(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
    @ExceptionHandler({HttpMediaTypeNotSupportedException.class})
    public CommonResponse handleHttpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e) {
        return defHandler(CommonRespCode.HTTP_MEDIA_TYPE_NOT_SUPPORT, null, e);
    }


    /**
     * BusinessException 业务异常处理
     * 返回状态码:500
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(BusinessException.class)
    public CommonResponse handleException(BusinessException e) {
        return defHandler(e.getCodeEnum(), e.getCodeMessage(), e);
    }

    /**
     * DefinitionException 自定义异常处理
     * 返回状态码:500
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(DefinitionException.class)
    public CommonResponse handleDefinitionException(DefinitionException e) {
        log.error("自定义异常", e);
        CommonResponse commonResponse = new CommonResponse();
        commonResponse.setCode(String.valueOf(e.getCode()));
        commonResponse.setMessage(e.getMessage());
        log.info("web_response:{}", JacksonUtils.toJson(commonResponse));
        return commonResponse;
    }


    /**
     * 所有异常统一处理
     * 返回状态码:500
     */
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    @ExceptionHandler(Exception.class)
    public CommonResponse handleException(Exception e) {
        return defHandler(CommonRespCode.SYSTEM_ERR, null, e);
    }

    /**
     * 异常转换为CommonResponse
     *
     * @param responseCode
     * @param appendMessage
     * @param e
     * @return
     */
    protected CommonResponse defHandler(ResponseCodeEnum responseCode, String appendMessage, Exception e) {
        log.error("服务异常", e);
        CommonResponse commonResponse = new CommonResponse();
        commonResponse.setCode(responseCode.getCode());
        if (responseCode instanceof BusinessException.UndefinedResponseCodeEnum) {
            commonResponse.setMessage(getResponseMessage(responseCode));
        } else if (org.apache.commons.lang3.StringUtils.isNotBlank(appendMessage)) {
            commonResponse.setMessage(getResponseMessage(responseCode) + "," + appendMessage);
        } else {
            commonResponse.setMessage(getResponseMessage(responseCode));
        }
        commonResponse.setTraceId(MDC.get(LogConstant.LOG_TRACE_ID));
        log.info("web_response:{}", JacksonUtils.toJson(commonResponse));
        return commonResponse;
    }

    /**
     * 获取响应码的描述信息
     *
     * @param responseCode
     * @return
     */
    protected String getResponseMessage(ResponseCodeEnum responseCode) {
        return responseCode.getMessage();
    }


    private AbstractCacheMap<CacheEntity> cacheEntityMap;

    protected String getResponseMessage2(ResponseCodeEnum responseCode) {
        String message = null;
        try {
            message = getCacheEntityValue(CommonCacheCategory.RESP_CODE, responseCode.getCode());
        } catch (Exception e) {
            log.warn("response message 获取失败", e);
        }
        if (org.apache.commons.lang3.StringUtils.isNotBlank(message)) {
            return message;
        }
        return responseCode.getMessage();
    }

    private String getCacheEntityValue(String category, String key) {
        if (cacheEntityMap == null) {
            cacheEntityMap = SpringUtil.getBean("cacheEntityMap");
        }
        if (cacheEntityMap == null) {
            return null;
        }
        CacheEntity cacheEntity = cacheEntityMap.get(category, key);
        return cacheEntity == null ? null : cacheEntity.getValue();
    }
}
